﻿namespace NetFx.SMIL.Definitions
{
  using System;
  using System.IO;
  using System.Text;
  using System.Threading;
  using System.Collections;
  using System.Collections.Generic;

  using System.Reflection;
  using System.Reflection.Emit;

  using System.Resources;
  using System.Globalization;
  using System.Security;
  using System.Security.Policy;
  using System.Security.Permissions;

  using NetFx.MSIL;
  using NetFx.SMIL.References;

  /// <summary>
  /// 程序集的定义。
  /// </summary>
  public class AssemblyDef : ReflectDef
  {
    #region Statics
    public static AssemblyDef it()
    {
      return new AssemblyDef();
    }
    public static AssemblyDef of(string name)
    {
      return new AssemblyDef(name);
    }
    public static AssemblyDef of(Assembly assembly)
    {
      return new AssemblyDef(assembly);
    }
    #endregion
    #region Fields
    protected AssemblyName assemblyName =null;
    protected AssemblyBuilderAccess access = AssemblyBuilderAccess.RunAndSave;
    protected string directory = string.Empty;
    protected Evidence evidence = null;
    protected PermissionSet required = null;
    protected PermissionSet optional = null;
    protected PermissionSet refused = null;
    protected bool isSynchronized = false;
    protected MethodRef entryPoint = null;
    protected ImageFileMachine imageFileMachine = ImageFileMachine.I386;
    protected List<ModuleDef> modules = null;
    #endregion
    #region Properties
    public override DefType DefinitionType
    {
      get { return DefType.Assembly; }
    }
    public virtual AssemblyName AssemblyName
    {
      get
      {
        return this.assemblyName;
      }
      set
      {
        this.assemblyName = value;
      }
    }
    public virtual AssemblyBuilderAccess Access
    {
      get
      {
        return this.access;
      }
      set
      {
        this.access = value;
      }
    }
    public virtual string Directory
    {
      get
      {
        return this.directory;
      }
      set
      {
        this.directory = value;
      }
    }
    public virtual Evidence Evidence
    {
      get
      {
        return this.evidence;
      }
      set
      {
        this.evidence = value;
      }
    }
    public virtual PermissionSet Required
    {
      get
      {
        return this.required;
      }
      set
      {
        this.required = value;
      }
    }
    public virtual PermissionSet Optional
    {
      get
      {
        return this.optional;
      }
      set
      {
        this.optional = value;
      }
    }
    public virtual PermissionSet Refused
    {
      get
      {
        return this.refused;
      }
      set
      {
        this.refused = value;
      }
    }
    public virtual bool IsSynchronized
    {
      get
      {
        return this.isSynchronized;
      }
      set
      {
        this.isSynchronized = value;
      }
    }
    public virtual MethodRef EntryPoint
    {
      get { return this.entryPoint; }
      set { this.entryPoint = value; }
    }
    public virtual ImageFileMachine ImageFileMachine
    {
      get
      {
        return this.imageFileMachine;
      }
      set
      {
        this.imageFileMachine = value;
      }
    }

    /// <summary>
    /// 获取程序集中的模块定义。
    /// </summary>
    public virtual List<ModuleDef> Modules
    {
      get
      {
        return this.modules == null ? 
          this.modules = new List<ModuleDef>() :
          this.modules;
      }
    }
    #endregion
    #region Constructors
    public AssemblyDef()
      : base()
    {
    }
    public AssemblyDef(string name):this(new AssemblyName(name))
    {

    }
    public AssemblyDef(AssemblyName name)
      : base(name.FullName)
    {

    }
    public AssemblyDef(AssemblyName name, AssemblyBuilderAccess access)
      : this(name)
    {
      this.access = access;
    }
    public AssemblyDef(AssemblyName name, AssemblyBuilderAccess access,bool isSynchronized)
      : this(name,access)
    {
      this.isSynchronized = isSynchronized;
    }
    public AssemblyDef(Assembly assembly)
    {
      this.OnReflecting(assembly);
    }
    #endregion
    #region Methods
    #region Defining
    public virtual ModuleDef DefineModule()
    {
      return this.AddModule(ModuleDef.of(this.AssemblyName.Name));
    }
    public virtual ModuleDef DefineModule(string name)
    {
      return this.AddModule(ModuleDef.of(name));
    }
    public virtual ModuleDef DefineModule(string name, string filename)
    {
      return this.AddModule(ModuleDef.of(name,filename));
    }
    public virtual ModuleDef DefineModule(string name, string filename, bool emitSymbol)
    {
      return this.AddModule(ModuleDef.of(name, filename,emitSymbol));
    }
    public virtual ModuleDef FindModule(string name)
    {
      if (name!=null && this.modules!=null)
      {
        foreach (ModuleDef module in this.Modules)
        {
          if (module != null && module.Name == name)
          {
            return module;
          }
        }
      }

      return null;
    }
    public virtual ModuleDef RemoveModule(string name)
    {
      return this.RemoveModule(this.FindModule(name));
    }
    #endregion
    #region Emiting
    public virtual AssemblyDef EmitAssembly()
    {
      return this.EmitAssembly(this.Name);
    }
    public virtual AssemblyDef EmitAssembly(string filename)
    {
      return this.EmitAssembly(filename, false);
    }
    public virtual AssemblyDef EmitAssembly(string filename, bool saveAsPossible)
    {
      return this.EmitAssembly(filename, saveAsPossible, false);
    }
    public virtual AssemblyDef EmitAssembly(string filename,bool saveAsPossible,bool reflectBack)
    {
      AssemblyGenerator assemblyGenerator = this.Generate(null) as AssemblyGenerator;
      if (saveAsPossible && ( this.Access == AssemblyBuilderAccess.RunAndSave | this.Access==AssemblyBuilderAccess.Save))
      {
        if (assemblyGenerator != null && assemblyGenerator.Builder != null)
        {
          assemblyGenerator.Builder.Save(filename);
        }
      }
      return (assemblyGenerator != null && reflectBack) ? (this.Reflect(assemblyGenerator.Builder) as AssemblyDef) : this;
    }
    #endregion
    #region Helpers
    protected virtual ModuleDef AddModule(ModuleDef module)
    {
      if (module != null)
      {
        module.Parent = this;

        this.Modules.Add(module);
      }
      return module;
    }
    protected virtual ModuleDef RemoveModule(ModuleDef module)
    {
      if (module != null)
      {
        module.Parent = null;

        this.Modules.Remove(module);
      }
      return module;
    }
    protected override AnyGenerator OnDefining(AnyGenerator container)
    {
      return AssemblyGenerator.DefineAssembly(
        this.AssemblyName,
        this.Access,
        this.Directory,
        this.Evidence,
        this.Required,
        this.Optional,
        this.Refused,
        this.IsSynchronized
        );
    }
    protected override AnyGenerator OnCompleting(AnyGenerator generator)
    {
      AssemblyGenerator g = generator as AssemblyGenerator;
      if (g != null)
      {
        this.GenerateList(g, this.Modules);

        g.SetEntryPoint(this.entryPoint.ReflectInfo as MethodInfo);
      }
      return base.OnCompleting(generator);
    }
    protected override ReflectDef OnReflecting(object source)
    {
      Assembly assembly = source as Assembly;
      if (assembly != null)
      {
        //DUMP OTHER VALUES!
        this.name = assembly.FullName;

        this.assemblyName = assembly.GetName();

        this.access = AssemblyBuilderAccess.RunAndSave; //ASSUME to SaveAndRun

        this.directory = assembly.CodeBase;
        this.evidence = assembly.Evidence;
        this.required = null;
        this.optional = null;
        this.refused = null;
        this.isSynchronized = true;
        this.entryPoint = MethodRef.of(assembly.EntryPoint);

        //if (assembly.ManifestModule != null && assembly.ManifestModule.ModuleHandle != null)
        //{
        //  assembly
        //    .ManifestModule
        //    .ModuleHandle
        //    .GetPEKind(out this.portableExcutableKind, out this.imageFileMachine);
        //}


        this.DisposeList(true);

        Module[] modules = assembly.GetModules(true);
        if (modules != null)
        {
          foreach (Module module in modules)
          {
            this.Modules.Add(ModuleDef.of(module));
          }
        }
      }
      return base.OnReflecting(assembly);
    }
    protected override void OnBreakingReference()
    {
      this.Break(this.EntryPoint);
      this.Break(this.Modules);
      base.OnBreakingReference();
    }
    protected override void DisposeList(bool disposing)
    {
      this.DisposeList(this.Modules);
    }
    #endregion
    #endregion
  }
}